/**    
 * 文件名：Ex8MeanShiftDetector.java    
 *    
 * 版本信息：    
 * 日期：2014年3月10日    
 * xyj 足下 xyj 2014     
 * 版权所有    
 *    
 */
package opencvtest.chapter04;

import static com.googlecode.javacv.cpp.opencv_core.CV_TERMCRIT_ITER;
import static com.googlecode.javacv.cpp.opencv_core.cvAnd;
import static com.googlecode.javacv.cpp.opencv_core.cvGetSize;
import static com.googlecode.javacv.cpp.opencv_highgui.CV_LOAD_IMAGE_COLOR;
import static com.googlecode.javacv.cpp.opencv_imgproc.CV_BGR2HSV;
import static com.googlecode.javacv.cpp.opencv_imgproc.CV_THRESH_BINARY;
import static com.googlecode.javacv.cpp.opencv_imgproc.cvCvtColor;
import static com.googlecode.javacv.cpp.opencv_imgproc.cvThreshold;
import static com.googlecode.javacv.cpp.opencv_video.cvMeanShift;
import static opencvtest.OpenCVUtils.drawOnImage;
import static opencvtest.OpenCVUtils.loadAndShowOrExit;
import static opencvtest.OpenCVUtils.show;
import static opencvtest.OpenCVUtils.toIplROI;
import static opencvtest.OpenCVUtils.toRectangle;

import java.awt.Color;
import java.awt.Rectangle;
import java.io.File;

import com.googlecode.javacv.cpp.opencv_core.CvRect;
import com.googlecode.javacv.cpp.opencv_core.CvTermCriteria;
import com.googlecode.javacv.cpp.opencv_core.IplImage;
import com.googlecode.javacv.cpp.opencv_imgproc.CvConnectedComp;
import com.googlecode.javacv.cpp.opencv_imgproc.CvHistogram;

/**
 * Uses the mean shift algorithm to find best matching location of the
 * 'template' in another image.
 * 
 * Matching is done using the hue channel of the input image converted to HSV
 * color space. Histogram of a region in the hue channel is used to create a
 * 'template'.
 * 
 * The target image, where we want to find a matching region, is also converted
 * to HSV. Histogram of the template is back projected in the hue channel. The
 * mean shift algorithm searches in the back projected image to find best match
 * to the template.
 * 
 * Example for section "Using the mean shift algorithm to find an object" in
 * Chapter 4.
 */
public class Ex8MeanShiftDetector {

    public static void main(String[] args) {

        //
        // Prepare 'template'
        //

        // Load image as a color
        IplImage templateImage = loadAndShowOrExit(new File("data/baboon1.jpg"), CV_LOAD_IMAGE_COLOR);

        // Display image with marked ROI
        Rectangle rect = new Rectangle(110, 260, 35, 40);
        show(drawOnImage(templateImage, rect, Color.RED), "Input template");

        // Define ROI for sample histogram
        templateImage.roi(toIplROI(rect));

        // Compute histogram within the ROI
        int minSaturation = 65;
        CvHistogram templateHueHist = new ColorHistogram().getHueHistogram(templateImage, minSaturation);

        //
        // Search a target image for best match to the 'template'
        //

        // Load the second image where we want to locate a new baboon face
        IplImage targetImage = loadAndShowOrExit(new File("data/baboon3.jpg"), CV_LOAD_IMAGE_COLOR);

        // Convert to HSV color space
        IplImage hsvTargetImage = IplImage.create(cvGetSize(targetImage), targetImage.depth(), 3);
        cvCvtColor(targetImage, hsvTargetImage, CV_BGR2HSV);

        // Identify pixels with low saturation
        IplImage saturationChannel = ColorHistogram.splitChannels(hsvTargetImage)[1];
        cvThreshold(saturationChannel, saturationChannel, minSaturation, 255, CV_THRESH_BINARY);
        show(saturationChannel, "Target saturation mask");

        // Get back-projection of the hue histogram of the 'template'
        ContentFinder finder = new ContentFinder();
        finder.histogram = templateHueHist;
        IplImage result = finder.find(hsvTargetImage);
        show(result, "Back-projection");

        // Eliminate low saturation pixels, to reduce noise abd improve search
        // quality
        cvAnd(result, saturationChannel, result, null);
        show(result, "Back-projection with reduced saturation pixels.");

        // Starting position for the search
        CvRect targetRect = new CvRect();
        targetRect.x(rect.x);
        targetRect.y(rect.y);
        targetRect.width(rect.width);
        targetRect.height(rect.height);

        // Search termination criteria
        CvTermCriteria termCriteria = new CvTermCriteria();
        termCriteria.max_iter(10);
        termCriteria.epsilon(0.01);
        termCriteria.type(CV_TERMCRIT_ITER);

        // Search using mean shift algorithm.
        CvConnectedComp searchResults = new CvConnectedComp();
        int iterations = cvMeanShift(result, targetRect, termCriteria, searchResults);
        show(drawOnImage(targetImage, toRectangle(searchResults.rect()), Color.RED), "Output in " + iterations
                + " iterations.");

    }
}
